// Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "paddle/ap/include/drr/native_ir_value_method_class.h"

namespace ap::drr {

struct SrcPtnNativeIrValueMethodClassImpl {
  using Self = drr::tSrcPtn<drr::NativeIrValue<drr::Node>>;
  using This = SrcPtnNativeIrValueMethodClassImpl;

  static adt::Result<axpr::Value> GetAttr(
      const axpr::Value& self_val, const std::vector<axpr::Value>& args) {
    ADT_LET_CONST_REF(self, self_val.template CastTo<Self>());
    ADT_CHECK(args.size() == 1);
    ADT_LET_CONST_REF(attr_name, args.at(0).template CastTo<std::string>());
    if (attr_name == "type") {
      ADT_LET_CONST_REF(opt_type,
                        OpTensorPatternCtxHelper{}.GetOptType(self.value()));
      if (opt_type.has_value()) {
        return opt_type.value();
      } else {
        return adt::Nothing{};
      }
    } else {
      return adt::errors::AttributeError{
          std::string() + "SrcPtnNativeIrValue '" + self.value()->name +
          "' has no attribute '" + attr_name + "'"};
    }
  }

  static adt::Result<axpr::Value> SetAttr(
      const axpr::Value& self_val, const std::vector<axpr::Value>& args) {
    ADT_LET_CONST_REF(self, self_val.template CastTo<Self>());
    ADT_CHECK(args.size() == 2);
    ADT_LET_CONST_REF(attr_name, args.at(0).template CastTo<std::string>());
    const auto& attr_val = args.at(1);
    if (attr_name == "type") {
      ADT_RETURN_IF_ERR(
          drr::OpTensorPatternCtxHelper{}.SetType(self.value(), attr_val));
      return adt::Nothing{};
    } else {
      return adt::errors::AttributeError{
          std::string() + "SrcPtnNativeIrValue '" + self.value()->name +
          "' has no attribute '" + attr_name + "'"};
    }
  }

  static adt::Result<axpr::Value> ToString(
      const axpr::Value& self_val, const std::vector<axpr::Value>& args) {
    ADT_LET_CONST_REF(self, self_val.template CastTo<Self>());
    std::ostringstream ss;
    const void* ptr = self.value().__adt_rc_shared_ptr_raw_ptr();
    ss << "<" << drr::Type<Self>{}.Name() << " object at " << ptr << ">";
    return ss.str();
  }

  static adt::Result<axpr::Value> Hash(const axpr::Value& self_val,
                                       const std::vector<axpr::Value>& args) {
    ADT_LET_CONST_REF(self, self_val.template CastTo<Self>());
    const void* ptr = self.value().__adt_rc_shared_ptr_raw_ptr();
    return reinterpret_cast<int64_t>(ptr);
  }

  static adt::Result<axpr::Value> Starred(
      const axpr::Value& self_val, const std::vector<axpr::Value>& args) {
    ADT_LET_CONST_REF(self, self_val.template CastTo<Self>());
    return adt::errors::TypeError{
        std::string() +
        "Only SrcPtnPackedIrValue and ResPtnPackedIrValue tensors can be "
        "unpacked. tensor '" +
        self.value()->name + "' is of type 'SrcPtnNativeIrValue'"};
  }
};

axpr::TypeImpl<axpr::BuiltinClassInstance<axpr::Value>>
GetSrcPtnNativeIrValueClass() {
  using Impl = SrcPtnNativeIrValueMethodClassImpl;
  using TT = drr::Type<drr::tSrcPtn<drr::NativeIrValue<drr::Node>>>;
  static auto cls(
      axpr::MakeBuiltinClass<axpr::Value>(TT{}.Name(), [&](const auto& Define) {
        Define("__str__", &Impl::ToString);
        Define("__hash__", &Impl::Hash);
        Define("__starred__", &Impl::Starred);
        Define("__getattr__", &Impl::GetAttr);
        Define("__setattr__", &Impl::SetAttr);
      }));
  using Self = typename Impl::Self;
  return axpr::MakeGlobalNaiveClassOps<Self>(cls);
}

struct ResPtnNativeIrValueMethodClass {
  using Self = drr::tResPtn<drr::NativeIrValue<drr::Node>>;
  using This = ResPtnNativeIrValueMethodClass;

  static adt::Result<axpr::Value> ToString(
      const axpr::Value& self_val, const std::vector<axpr::Value>& args) {
    ADT_LET_CONST_REF(self, self_val.template CastTo<Self>());
    std::ostringstream ss;
    const void* ptr = self.value().__adt_rc_shared_ptr_raw_ptr();
    ss << "<" << drr::Type<Self>{}.Name() << " object at " << ptr << ">";
    return ss.str();
  }

  static adt::Result<axpr::Value> Hash(const axpr::Value& self_val,
                                       const std::vector<axpr::Value>& args) {
    ADT_LET_CONST_REF(self, self_val.template CastTo<Self>());
    const void* ptr = self.value().__adt_rc_shared_ptr_raw_ptr();
    return reinterpret_cast<int64_t>(ptr);
  }

  static adt::Result<axpr::Value> Starred(
      const axpr::Value& self_val, const std::vector<axpr::Value>& args) {
    ADT_LET_CONST_REF(self, self_val.template CastTo<Self>());
    return adt::errors::TypeError{
        std::string() +
        "Only SrcPtnPackedIrValue and ResPtnPackedIrValue tensors can be "
        "unpacked. tensor '" +
        self.value()->name + "' is of type 'ResPtnNativeIrValue'"};
  }
};

axpr::TypeImpl<axpr::BuiltinClassInstance<axpr::Value>>
GetResPtnNativeIrValueClass() {
  using Impl = ResPtnNativeIrValueMethodClass;
  using TT = drr::Type<drr::tResPtn<drr::NativeIrValue<drr::Node>>>;
  static auto cls(
      axpr::MakeBuiltinClass<axpr::Value>(TT{}.Name(), [&](const auto& Define) {
        Define("__str__", &Impl::ToString);
        Define("__hash__", &Impl::Hash);
        Define("__starred__", &Impl::Starred);
      }));
  using Self = typename Impl::Self;
  return axpr::MakeGlobalNaiveClassOps<Self>(cls);
}

}  // namespace ap::drr
